/**
 * 
 */
package gopi.labs.database;

import gopi.labs.Connector.NameValue;
import gopi.labs.exceptions.InValidIdentifier;
import gopi.labs.exceptions.InvalidDataRecord;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * @author GS4
 * 
 */
public abstract class DataStream extends Database {

	private String cloudSpace;
	private String dataModel;
	@SuppressWarnings("unused")
	private long size;
	@SuppressWarnings("unused")
	private long securityGroup;

	private List<DataStreamMapping> dataStreamMapping;

	
	public DataStream(String name) {
		super(name);
		this.dataStreamMapping = new ArrayList<DataStreamMapping>();
	}

	public abstract void create() throws SQLException, InvalidDataRecord;

	public abstract void alter() throws SQLException, InvalidDataRecord;

	public abstract void drop() throws SQLException;

	@Override
	public void validate() throws InValidIdentifier, SQLException {
		super.validate();

		if (this.getCloudSpace() == null
				|| this.getCloudSpace().equalsIgnoreCase(""))
			throw new InValidIdentifier(
					"CloudSpace for a dataStream cannot be null");
		if (this.getCloudSpace() != null
				&& !this.checkCloudExists(this.getCloudSpace()))
			throw new InValidIdentifier("CloudSpace " + this.getCloudSpace()
					+ " does not exists");
		if (this.getDataModel() == null
				|| this.getDataModel().equalsIgnoreCase(""))
			throw new InValidIdentifier(
					"DataModel for a dataStream cannot be null");
		if (this.getDataModel() != null
				&& !this.checkDataModelExists(this.getDataModel(),
						this.getCloudSpace()))
			throw new InValidIdentifier("DataModel  " + this.getDataModel()
					+ " does not exists ");

		if (this.getDataStreamMapping() == null
				|| this.getDataStreamMapping().size() == 0)
			throw new InValidIdentifier(
					"No Mapping details available for the data stream");

		if (this.getDataStreamMapping() != null) {
			for (int i = 0; i < this.getDataStreamMapping().size(); i++)
				if (!checkColumnExists(this.getDataStreamMapping().get(i)
						.getSrcColumnName()))
					throw new InValidIdentifier("Source column does not exist "
							+ this.getDataStreamMapping().get(i)
									.getSrcColumnName());
		}
	}

	private boolean checkColumnExists(String columnName) throws SQLException {
		Statement stmt = this.connection.createStatement();

		ResultSet rs = stmt
				.executeQuery("select count(*) from information_schema.columns where table_schema = '"
						+ this.getCloudSpace()
						+ "' and table_name = '"
						+ this.getDataModel()
						+ "' and column_name = '"
						+ columnName + "'");

		if (rs.next() && rs.getInt(1) == 0)
			return false;

		return true;

	}

	public List<NameValue> getAttributeDetails() throws SQLException,
			InvalidDataRecord {
		List<NameValue> dataStreamDetails = new ArrayList<NameValue>();
		Statement stmt = this.connection.createStatement();
		if (!checkDataModelExists(this.getDataModel(), this.getCloudSpace()))
			throw new InvalidDataRecord("DataModel name for data stream"
					+ this.getName() + " does not exist");

		ResultSet rs = stmt
				.executeQuery("select * from metadata.data_stream where name = '"
						+ this.getName()
						+ "' and cloud_name = '"
						+ this.getCloudSpace()
						+ "' and data_model_name = '"
						+ this.getDataModel() + "'");

		while (rs.next()) {
			for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
				dataStreamDetails.add(new NameValue(rs.getMetaData()
						.getColumnLabel(i), rs.getString(i)));
			}
		}
		rs = stmt
				.executeQuery("select * from metadata.data_stream_attributes where name = '"
						+ this.getName()
						+ "' and cloud_name = '"
						+ this.getCloudSpace()
						+ "' and data_model_name ='"
						+ this.getDataModel() + "'");

		while (rs.next()) {
			dataStreamDetails.add(new NameValue(rs.getString("attribute"), rs
					.getString("value")));
		}
		return dataStreamDetails;

	}

	@Override
	public void insertUpdateAttributes(List<NameValue> attributes)
			throws SQLException {
		Statement stmt = this.connection.createStatement();
		for (int i = 0; i < attributes.size(); i++) {
			ResultSet rs = stmt
					.executeQuery("select count(*) from metadata.data_stream_attributes"
							+ " where name = '"
							+ this.getName()
							+ "' and cloud_name = '"
							+ this.getCloudSpace()
							+ "' and data_model_name = '"
							+ this.getDataModel()
							+ "' and attribute='"
							+ attributes.get(i).getName().toString() + "'");
			rs.next();
			if (rs.getInt(1) == 0) {
				stmt.execute("insert into metadata.data_stream_attributes"
						+ " (cloud_name, data_model_name, name, attribute, value) values "
						+ "('" + this.getCloudSpace() + "','"
						+ this.getDataModel() + "','" + this.getName() + "','"
						+ attributes.get(i).getName() + "','"
						+ attributes.get(i).getValue() + "')");
			} else {
				stmt.execute("update metadata.data_stream_attributes set value =  '"
						+ attributes.get(i).getValue()
						+ "' where cloud_name = '"
						+ this.getCloudSpace()
						+ "' and data_model_name = '"
						+ this.getDataModel()
						+ "' and name = '"
						+ this.getName()
						+ "' and attribute = '"
						+ attributes.get(i).getName()
						+ "'");
			}
			rs.close();
		}
	}

	public String getDataModel() {
		return dataModel;
	}

	public void setDataModel(String dataModel) {
		this.dataModel = dataModel.toUpperCase();
	}

	public String getCloudSpace() {
		return cloudSpace;
	}

	public void setCloudSpace(String cloudSpace) {
		this.cloudSpace = cloudSpace.toUpperCase();
	}

	public List<DataStreamMapping> getDataStreamMapping() {
		return dataStreamMapping;
	}

	public void setDataStreamMapping(List<DataStreamMapping> dataStreamMapping) {
		this.dataStreamMapping = dataStreamMapping;
	}

	public void addDataStreamMapping(String srcColumn, String dstColumn,
			int position) {
		this.dataStreamMapping.add(new DataStreamMapping(srcColumn, dstColumn,
				position));

	}

	public void addDataStreamMapping(String srcColumn, int position) {
		this.dataStreamMapping.add(new DataStreamMapping(srcColumn, srcColumn,
				position));

	}
}
